export function isUndef(v) {
	return v === undefined || v === null
}
export function isDef(v) {
	return v !== undefined && v !== null
}
export function isTrue(v) {
	return v === true
}
export function isFalse(v) {
	return v === false
}
export function isPrimitive(value) {
	return (
		typeof value === 'string' ||
		typeof value === 'number' ||
		typeof value === 'symbol' ||
		typeof value === 'boolean'
	)
}
export function isObject(obj) {
	return obj !== null && typeof obj === 'object'
}

var _toString = Object.prototype.toString;

export function toRawType(value) {
	return _toString.call(value).slice(8, -1)
}

export function isPlainObject(obj) {
	return _toString.call(obj) === '[object Object]'
}

export function isRegExp(v) {
	return _toString.call(v) === '[object RegExp]'
}

export function isValidArrayIndex(val) {
	var n = parseFloat(String(val));
	return n >= 0 && Math.floor(n) === n && isFinite(val)
}

export function isPromise(val) {
	return (
		isDef(val) &&
		typeof val.then === 'function' &&
		typeof val.catch === 'function'
	)
}

export function toString(val) {
	return val == null
		? ''
		: Array.isArray(val) || (isPlainObject(val) && val.toString === _toString)
			? JSON.stringify(val, null, 2)
			: String(val)
}

/**
 * Convert an input value to a number for persistence.
 * If the conversion fails, return original string.
 */
export function toNumber(val) {
	var n = parseFloat(val);
	return isNaN(n) ? val : n
}
// Make a map and return a function for checking if a key is in that map.
export function makeMap(
	str,
	expectsLowerCase
) {
	var map = Object.create(null);
	var list = str.split(',');
	for (var i = 0; i < list.length; i++) {
		map[list[i]] = true;
	}
	return expectsLowerCase
		? function (val) { return map[val.toLowerCase()]; }
		: function (val) { return map[val]; }
}
// Remove an item from an array.
export function remove(arr, item) {
	if (arr.length) {
		var index = arr.indexOf(item);
		if (index > -1) {
			return arr.splice(index, 1)
		}
	}
}

/**
	* Check whether an object has the property.
	*/
export function hasOwn(obj, key) {
	return Object.prototype.hasOwnProperty.call(obj, key)
}
//  Create a cached version of a pure function.
export function cached(fn) {
	var cache = Object.create(null);
	return (function cachedFn(str) {
		var hit = cache[str];
		return hit || (cache[str] = fn(str))
	})
}
export function polyfillBind(fn, ctx) {
	function boundFn(a) {
		var l = arguments.length;
		return l
			? l > 1
				? fn.apply(ctx, arguments)
				: fn.call(ctx, a)
			: fn.call(ctx)
	}

	boundFn._length = fn.length;
	return boundFn
}

export function nativeBind(fn, ctx) {
	return fn.bind(ctx)
}

// Convert an Array-like object to a real Array.
export function toArray(list, start) {
	start = start || 0;
	var i = list.length - start;
	var ret = new Array(i);
	while (i--) {
		ret[i] = list[i + start];
	}
	return ret
}

// Mix properties into target object.
export function extend(to, _from) {
	for (var key in _from) {
		to[key] = _from[key];
	}
	return to
}

// Merge an Array of Objects into a single Object.
export function toObject(arr) {
	var res = {};
	for (var i = 0; i < arr.length; i++) {
		if (arr[i]) {
			extend(res, arr[i]);
		}
	}
	return res
}
// 不执行任何操作。
export function noop(a, b, c) { }
// 从编译器模块生成包含静态键的字符串。
export function genStaticKeys(modules) {
	return modules.reduce(function (keys, m) {
		return keys.concat(m.staticKeys || [])
	}, []).join(',')
}

// 检查两个值是否大致相等
export function looseEqual(a, b) {
	if (a === b) { return true }
	var isObjectA = isObject(a);
	var isObjectB = isObject(b);
	if (isObjectA && isObjectB) {
		try {
			var isArrayA = Array.isArray(a);
			var isArrayB = Array.isArray(b);
			if (isArrayA && isArrayB) {
				return a.length === b.length && a.every(function (e, i) {
					return looseEqual(e, b[i])
				})
			} else if (a instanceof Date && b instanceof Date) {
				return a.getTime() === b.getTime()
			} else if (!isArrayA && !isArrayB) {
				var keysA = Object.keys(a);
				var keysB = Object.keys(b);
				return keysA.length === keysB.length && keysA.every(function (key) {
					return looseEqual(a[key], b[key])
				})
			} else {
				return false
			}
		} catch (e) {
			return false
		}
	} else if (!isObjectA && !isObjectB) {
		return String(a) === String(b)
	} else {
		return false
	}
}

// 返回第一个索引，在该索引处可以在数组中找到松散相等的值（如果值是普通对象，
// 则数组必须包含相同形状的对象），如果不存在，则返回-1。
export function looseIndexOf(arr, val) {
	for (var i = 0; i < arr.length; i++) {
		if (looseEqual(arr[i], val)) { return i }
	}
	return -1
}
// 确保函数只被调用一次。
export function once(fn) {
	var called = false;
	return function () {
		if (!called) {
			called = true;
			fn.apply(this, arguments);
		}
	}
}
// 检查字符串是否以$或开头_
export function isReserved(str) {
	var c = (str + '').charCodeAt(0);
	return c === 0x24 || c === 0x5F
}
// Define a property.
export function def(obj, key, val, enumerable) {
	Object.defineProperty(obj, key, {
		value: val,
		enumerable: !!enumerable,
		writable: true,
		configurable: true
	});
}
/* istanbul ignore next */
export function isNative(Ctor) {
	return typeof Ctor === 'function' && /native code/.test(Ctor.toString())
}
// 通过使用__proto拦截原型链来增强目标对象或数组__
export function protoAugment(target, src) {
	target.__proto__ = src;
}
// 通过定义隐藏属性来增强目标对象或数组。
export function copyAugment(target, src, keys) {
	for (var i = 0, l = keys.length; i < l; i++) {
		var key = keys[i];
		def(target, key, src[key]);
	}
}

export function dedupeHooks(hooks) {
	var res = [];
	for (var i = 0; i < hooks.length; i++) {
		if (res.indexOf(hooks[i]) === -1) {
			res.push(hooks[i]);
		}
	}
	return res
}
// Hooks and props are merged as arrays.
export function mergeHook(
	parentVal,
	childVal
) {
	var res = childVal
		? parentVal
			? parentVal.concat(childVal)
			: Array.isArray(childVal)
				? childVal
				: [childVal]
		: parentVal;
	return res
		? dedupeHooks(res)
		: res
}

// 将原始函数指令规范化为对象格式
export function normalizeDirectives(options) {
	var dirs = options.directives;
	if (dirs) {
		for (var key in dirs) {
			var def$$1 = dirs[key];
			if (typeof def$$1 === 'function') {
				dirs[key] = { bind: def$$1, update: def$$1 };
			}
		}
	}
}

var functionTypeCheckRE = /^\s*function (\w+)/;
// 使用函数字符串名称检查内置类型，因为在不同的vms/iframe之间运行时，简单的相等性检查将失败。
export function getType(fn) {
	var match = fn && fn.toString().match(functionTypeCheckRE);
	return match ? match[1] : ''
}

export function isSameType(a, b) {
	return getType(a) === getType(b)
}

export function getTypeIndex(type, expectedTypes) {
	if (!Array.isArray(expectedTypes)) {
		return isSameType(expectedTypes, type) ? 0 : -1
	}
	for (var i = 0, len = expectedTypes.length; i < len; i++) {
		if (isSameType(expectedTypes[i], type)) {
			return i
		}
	}
	return -1
}

var EXPLICABLE_TYPES = ['string', 'number', 'boolean'];
export function isExplicable(value) {
	return EXPLICABLE_TYPES.some(function (elem) { return value.toLowerCase() === elem; })
}

export function isBoolean() {
	var args = [], len = arguments.length;
	while (len--) args[len] = arguments[len];

	return args.some(function (elem) { return elem.toLowerCase() === 'boolean'; })
}
function styleValue(value, type) {
	if (type === 'String') {
		return ("\"" + value + "\"")
	} else if (type === 'Number') {
		return ("" + (Number(value)))
	} else {
		return ("" + value)
	}
}
export function getInvalidTypeMessage(name, value, expectedTypes) {
	var message = "Invalid prop: type check failed for prop \"" + name + "\"." +
		" Expected " + (expectedTypes.map(capitalize).join(', '));
	var expectedType = expectedTypes[0];
	var receivedType = toRawType(value);
	// check if we need to specify expected value
	if (
		expectedTypes.length === 1 &&
		isExplicable(expectedType) &&
		isExplicable(typeof value) &&
		!isBoolean(expectedType, receivedType)
	) {
		message += " with value " + (styleValue(value, expectedType));
	}
	message += ", got " + receivedType + " ";
	// check if we need to specify received value
	if (isExplicable(receivedType)) {
		message += "with value " + (styleValue(value, receivedType)) + ".";
	}
	return message
}

export var normalizeEvent = cached(function (name) {
	var passive = name.charAt(0) === '&';
	name = passive ? name.slice(1) : name;
	var once$$1 = name.charAt(0) === '~'; // Prefixed last, checked first
	name = once$$1 ? name.slice(1) : name;
	var capture = name.charAt(0) === '!';
	name = capture ? name.slice(1) : name;
	return {
		name: name,
		once: once$$1,
		capture: capture,
		passive: passive
	}
});

export function checkProp(
	res,
	hash,
	key,
	altKey,
	preserve
) {
	if (isDef(hash)) {
		if (hasOwn(hash, key)) {
			res[key] = hash[key];
			if (!preserve) {
				delete hash[key];
			}
			return true
		} else if (hasOwn(hash, altKey)) {
			res[key] = hash[altKey];
			if (!preserve) {
				delete hash[altKey];
			}
			return true
		}
	}
	return false
}

export function isTextNode(node) {
	return isDef(node) && isDef(node.text) && isFalse(node.isComment)
}
export function initProvide(vm) {
	var provide = vm.$options.provide;
	if (provide) {
		vm._provided = typeof provide === 'function'
			? provide.call(vm)
			: provide;
	}
}

export function isWhitespace(node) {
	return (node.isComment && !node.asyncFactory) || node.text === ' '
}

export function isAsyncPlaceholder(node) {
	return node.isComment && node.asyncFactory
}

export function proxyNormalSlot(slots, key) {
	return function () { return slots[key]; }
}
export function isKeyNotMatch(expect, actual) {
	if (Array.isArray(expect)) {
		return expect.indexOf(actual) === -1
	} else {
		return expect !== actual
	}
}
export function markStatic(
	tree,
	key,
	isOnce
) {
	if (Array.isArray(tree)) {
		for (var i = 0; i < tree.length; i++) {
			if (tree[i] && typeof tree[i] !== 'string') {
				markStaticNode(tree[i], (key + "_" + i), isOnce);
			}
		}
	} else {
		markStaticNode(tree, key, isOnce);
	}
}

export function markStaticNode(node, key, isOnce) {
	node.isStatic = true;
	node.key = key;
	node.isOnce = isOnce;
}

export function resolveScopedSlots(
	fns, // see flow/vnode
	res,
	// 2.6中添加了以下内容
	hasDynamicKeys,
	contentHashKey
) {
	res = res || { $stable: !hasDynamicKeys };
	for (var i = 0; i < fns.length; i++) {
		var slot = fns[i];
		if (Array.isArray(slot)) {
			resolveScopedSlots(slot, res, hasDynamicKeys);
		} else if (slot) {
			// marker for reverse proxying v-slot without scope on this.$slots
			if (slot.proxy) {
				slot.fn.proxy = true;
			}
			res[slot.key] = slot.fn;
		}
	}
	if (contentHashKey) {
		(res).$key = contentHashKey;
	}
	return res
}
// 用于将修饰符运行时标记动态附加到事件名称的助手。
// 当值已经是字符串时，请确保仅追加，否则它将被强制转换为字符串并导致类型检查失败
export function prependModifier(value, symbol) {
	return typeof value === 'string' ? symbol + value : value
}

export function createElement$1(tagName, vnode) {
	var elm = document.createElement(tagName);
	if (tagName !== 'select') {
		return elm
	}
	// false or null will remove the attribute but undefined will not
	if (vnode.data && vnode.data.attrs && vnode.data.attrs.multiple !== undefined) {
		elm.setAttribute('multiple', 'multiple');
	}
	return elm
}

export function createElementNS(namespace, tagName) {
	return document.createElementNS(namespaceMap[namespace], tagName)
}

export function createTextNode(text) {
	return document.createTextNode(text)
}

export function createComment(text) {
	return document.createComment(text)
}

export function insertBefore(parentNode, newNode, referenceNode) {
	parentNode.insertBefore(newNode, referenceNode);
}

export function removeChild(node, child) {
	node.removeChild(child);
}

export function appendChild(node, child) {
	node.appendChild(child);
}

export function parentNode(node) {
	return node.parentNode
}

export function nextSibling(node) {
	return node.nextSibling
}

export function tagName(node) {
	return node.tagName
}

export function setTextContent(node, text) {
	node.textContent = text;
}

export function setStyleScope(node, scopeId) {
	node.setAttribute(scopeId, '');
}

export function initInternalComponent(vm, options) {
	var opts = vm.$options = Object.create(vm.constructor.options);
	// 这样做是因为它比动态枚举更快。
	var parentVnode = options._parentVnode;
	opts.parent = options.parent;
	opts._parentVnode = parentVnode;

	var vnodeComponentOptions = parentVnode.componentOptions;
	opts.propsData = vnodeComponentOptions.propsData;
	opts._parentListeners = vnodeComponentOptions.listeners;
	opts._renderChildren = vnodeComponentOptions.children;
	opts._componentTag = vnodeComponentOptions.tag;

	if (options.render) {
		opts.render = options.render;
		opts.staticRenderFns = options.staticRenderFns;
	}
}

export function resolveModifiedOptions(Ctor) {
	var modified;
	var latest = Ctor.options;
	var sealed = Ctor.sealedOptions;
	for (var key in latest) {
		if (latest[key] !== sealed[key]) {
			if (!modified) { modified = {}; }
			modified[key] = latest[key];
		}
	}
	return modified
}

export function initUse(Vue) {
	Vue.use = function (plugin) {
		var installedPlugins = (this._installedPlugins || (this._installedPlugins = []));
		if (installedPlugins.indexOf(plugin) > -1) {
			return this
		}

		// additional parameters
		var args = toArray(arguments, 1);
		args.unshift(this);
		if (typeof plugin.install === 'function') {
			plugin.install.apply(plugin, args);
		} else if (typeof plugin === 'function') {
			plugin.apply(null, args);
		}
		installedPlugins.push(plugin);
		return this
	};
}
export function getComponentName(opts) {
	return opts && (opts.Ctor.options.name || opts.tag)
}

export function matches(pattern, name) {
	if (Array.isArray(pattern)) {
		return pattern.indexOf(name) > -1
	} else if (typeof pattern === 'string') {
		return pattern.split(',').indexOf(name) > -1
	} else if (isRegExp(pattern)) {
		return pattern.test(name)
	}
	return false
}
export function pruneCache(keepAliveInstance, filter) {
	var cache = keepAliveInstance.cache;
	var keys = keepAliveInstance.keys;
	var _vnode = keepAliveInstance._vnode;
	for (var key in cache) {
		var entry = cache[key];
		if (entry) {
			var name = entry.name;
			if (name && !filter(name)) {
				pruneCacheEntry(cache, key, keys, _vnode);
			}
		}
	}
}
export function pruneCacheEntry(
	cache,
	key,
	keys,
	current
) {
	var entry = cache[key];
	if (entry && (!current || entry.tag !== current.tag)) {
		entry.componentInstance.$destroy();
	}
	cache[key] = null;
	remove(keys, key);
}

export function genClassForVnode(vnode) {
	var data = vnode.data;
	var parentNode = vnode;
	var childNode = vnode;
	while (isDef(childNode.componentInstance)) {
		childNode = childNode.componentInstance._vnode;
		if (childNode && childNode.data) {
			data = mergeClassData(childNode.data, data);
		}
	}
	while (isDef(parentNode = parentNode.parent)) {
		if (parentNode && parentNode.data) {
			data = mergeClassData(data, parentNode.data);
		}
	}
	return renderClass(data.staticClass, data.class)
}

export function mergeClassData(child, parent) {
	return {
		staticClass: concat(child.staticClass, parent.staticClass),
		class: isDef(child.class)
			? [child.class, parent.class]
			: parent.class
	}
}

export function renderClass(
	staticClass,
	dynamicClass
) {
	if (isDef(staticClass) || isDef(dynamicClass)) {
		return concat(staticClass, stringifyClass(dynamicClass))
	}
	return ''
}

export function concat(a, b) {
	return a ? b ? (a + ' ' + b) : a : (b || '')
}

export function stringifyClass(value) {
	if (Array.isArray(value)) {
		return stringifyArray(value)
	}
	if (isObject(value)) {
		return stringifyObject(value)
	}
	if (typeof value === 'string') {
		return value
	}
	return ''
}

export function stringifyArray(value) {
	var res = '';
	var stringified;
	for (var i = 0, l = value.length; i < l; i++) {
		if (isDef(stringified = stringifyClass(value[i])) && stringified !== '') {
			if (res) { res += ' '; }
			res += stringified;
		}
	}
	return res
}

export function stringifyObject(value) {
	var res = '';
	for (var key in value) {
		if (value[key]) {
			if (res) { res += ' '; }
			res += key;
		}
	}
	return res
}

export function registerRef(vnode, isRemoval) {
	var key = vnode.data.ref;
	if (!isDef(key)) { return }

	var vm = vnode.context;
	var ref = vnode.componentInstance || vnode.elm;
	var refs = vm.$refs;
	if (isRemoval) {
		if (Array.isArray(refs[key])) {
			remove(refs[key], ref);
		} else if (refs[key] === ref) {
			refs[key] = undefined;
		}
	} else {
		if (vnode.data.refInFor) {
			if (!Array.isArray(refs[key])) {
				refs[key] = [ref];
			} else if (refs[key].indexOf(ref) < 0) {
				refs[key].push(ref);
			}
		} else {
			refs[key] = ref;
		}
	}
}

export function createKeyToOldIdx(children, beginIdx, endIdx) {
	var i, key;
	var map = {};
	for (i = beginIdx; i <= endIdx; ++i) {
		key = children[i].key;
		if (isDef(key)) { map[key] = i; }
	}
	return map
}
export function isPatchable(vnode) {
	while (vnode.componentInstance) {
		vnode = vnode.componentInstance._vnode;
	}
	return isDef(vnode.tag)
}